﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ZOVNP.Model;
using ZOVNP.Model.Abstracts;
using Fintech.Utils;

namespace ZOVNP.WinView.Workplaces.Seller
{
    public partial class OrderFullEditControl : UserControl
    {
        private class PartNode
        {
            public PartNode(IOrderPartHelp part)
            {
                Part = part;
                Childs = new List<PartNode>();
            }

            public readonly IOrderPartHelp Part;
            public PartNode Parent { get; set; }
            public List<PartNode> Childs { get; private set; }
        }

        private class OrderPartHelpViewInfo
        {
            public OrderPartHelpViewInfo(DataGridView grid, Button btn, bool collapsed)
            {
                Collapsed = collapsed;
                Grid = grid;
                Btn = btn;
            }

            public readonly Button Btn;
            public readonly DataGridView Grid;
            public bool Collapsed { get; set; }
        }

        private IOrder _order;
        private OrderEditControl _oec;
        private TableLayoutPanel _tlp;
        private Dictionary<IOrderPartHelp, OrderPartHelpViewInfo> _grids;
        private IEnumerable<IPriceItem> _priceItems;
        private Dictionary<string, IProduct> _products;
        private Dictionary<string, IMaterial> _materials;
        private Dictionary<Guid, IProduct> _cacheProducts;
        private Dictionary<Guid, IMaterial> _cacheMaterials;
        private Dictionary<Guid, Tuple<DataGridViewComboBoxCell, DataGridViewComboBoxCell>> _templates;
        //private DataGridViewComboBoxCell _productComboBoxTemplate;
        //private DataGridViewComboBoxCell _materialComboBoxTemplate;
        private Lazy<ColumnIndexableDataTableContainer> _priceItemsCombinations;
        private bool _inEditMode = false;

        public OrderFullEditControl(IOrder order, Workspace workspace)
        {
            _templates = new Dictionary<Guid, Tuple<DataGridViewComboBoxCell, DataGridViewComboBoxCell>>();
            _grids = new Dictionary<IOrderPartHelp, OrderPartHelpViewInfo>();

            InitializeComponent();
            
            this.AutoScroll = true;
            
            var mainTlp = new TableLayoutPanel();
            mainTlp.ColumnCount = 1;
            mainTlp.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
            mainTlp.RowCount = 2;
            mainTlp.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            mainTlp.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
            Controls.Add(mainTlp);
            mainTlp.AutoSize = true;
            mainTlp.Dock = DockStyle.Top;

            _oec = new OrderEditControl(order, workspace);
            mainTlp.Controls.Add(_oec, 0, 0);
            _oec.Dock = DockStyle.Fill;

            _tlp = new TableLayoutPanel();
            _tlp.ColumnCount = 2;
            _tlp.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
            _tlp.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 40));
            
            GroupBox gbx = new GroupBox();
            gbx.Text = "Состав заказа";
            gbx.Dock = DockStyle.Fill;
            gbx.Controls.Add(_tlp);
            gbx.AutoSize = true;

            mainTlp.Controls.Add(gbx, 0, 1);
            _tlp.AutoSize = true;
            _tlp.Dock = DockStyle.Top;

            PopulatePriceInfo(workspace);
            PopulateOrderParts(workspace);

            Order = order;
        }

        public IOrder Order
        {
            get { return _order; }
            set
            {
                _order = value;
                SetOrderToView();
            }
        }

        public event EventHandler<EventArgs<IOrderItem>> OrderItemAdded;

        public void InvokeOrderItemAdded(EventArgs<IOrderItem> e)
        {
            EventHandler<EventArgs<IOrderItem>> handler = OrderItemAdded;
            if (handler != null) handler(this, e);
        }

        public event EventHandler<EventArgs<IOrderPart>> OrderPartAdded;

        public void InvokeOrderPartAdded(EventArgs<IOrderPart> e)
        {
            EventHandler<EventArgs<IOrderPart>> handler = OrderPartAdded;
            if (handler != null) handler(this, e);
        }

        private void SetOrderToView()
        {
            if (_oec != null)
                _oec.Order = _order;

            _grids.ForEach(g => g.Value.Grid.Rows.Clear());

            if(Order != null)
            {
                foreach (var orderPart in Order.OrderParts)
                {
                    var grid = AddPart(new PartNode(orderPart.OrderPartHelp));
                    bool added = false;
                    foreach (var orderItem in orderPart.OrderItems)
                    {
                        DataGridViewRow row = new DataGridViewRow();
                        row.CreateCells(grid);
                        row.Cells[grid.Columns["ProductName"].Index].Value = orderItem.PriceItem.Product.Name;
                        row.Cells[grid.Columns["MaterialName"].Index].Value = orderItem.PriceItem.Material.Name;
                        row.Cells[grid.Columns["Amount"].Index].Value = orderItem.Amount;
                        row.Cells[grid.Columns["Description"].Index].Value = orderItem.Description;
                        row.Cells[grid.Columns["Cost"].Index].Value = orderItem.GetCost();
                        row.Tag = orderItem;
                        grid.Rows.Add(row);
                        added = true;
                    }
                    //if (grid.Rows.Count > 0)
                    SetCollapsedState(_grids[orderPart.OrderPartHelp], !added);
                }
            }
        }

        public void PopulatePriceInfo(Workspace workspace)
        {
            using (var context = workspace.Engine.CreateContext())
            {
                var parts = context.Getter.GetAllOrderPartHelps();
                var priceItems = context.Getter.GetAllPriceItemsFull();
                Func<ColumnIndexableDataTableContainer> combinations = () => new ColumnIndexableDataTableContainer(workspace.Engine.Getter.GetProductMaterialPriceItemsCombinations());

                _products = new Dictionary<string, IProduct>();
                _materials = new Dictionary<string, IMaterial>();
                _cacheProducts = new Dictionary<Guid, IProduct>();
                _cacheMaterials = new Dictionary<Guid, IMaterial>();

                _priceItems = new List<IPriceItem>(priceItems);

                var materials = context.Query<IMaterial>();
                var products = context.Query<IProduct>().Include(p => p.ProductType);
                materials.ForEach(m =>
                    {
                        _materials.Add(m.Name, m);
                        _cacheMaterials.Add(m.ID, m);
                    });
                products.ForEach(p =>
                    {
                        _products.Add(p.GetKeyName(), p);
                        _cacheProducts.Add(p.ID, p);
                    });

                foreach (var orderPartHelp in parts)
                {
                    Tuple<DataGridViewComboBoxCell, DataGridViewComboBoxCell> templates;
                    if (!_templates.TryGetValue(orderPartHelp.ID, out templates))
                    {
                        var productComboBoxTemplate = new DataGridViewComboBoxCell();
                        var materialComboBoxTemplate = new DataGridViewComboBoxCell();

                        //var productsOfThisPart = context.Query<IProduct>().OrderBy(p => p.Name);
                        productComboBoxTemplate.Items.AddRange(products);
                        materialComboBoxTemplate.Items.AddRange(materials);

                        templates = new Tuple<DataGridViewComboBoxCell, DataGridViewComboBoxCell>(productComboBoxTemplate, materialComboBoxTemplate);
                        _templates.Add(orderPartHelp.ID, templates);
                    }
                }

                if (combinations != null)
                    _priceItemsCombinations = new Lazy<ColumnIndexableDataTableContainer>(combinations);
            }
        }

        public void PopulateOrderParts(Workspace workspace)
        {
            var parts = workspace.GetAllOrderPartHelps();
            List<IOrderPartHelp> partsList = new List<IOrderPartHelp>(parts);
            var root = partsList.FirstOrDefault(p => p.ParentPart == null);
            if (root == null)
                throw new ArgumentException("Parts must have one root.");
            partsList.Remove(root);
            var rootNode = new PartNode(root);
            PopulatePartsNodesRecursive(rootNode, partsList);
        }

        private void PopulatePartsNodesRecursive(PartNode node, List<IOrderPartHelp> parts)
        {
            var childs = parts.Where(p => p.ParentPartID == node.Part.ID).ToArray();
            childs.OrderBy(c => c.OrderNumber).ForEach(c =>
                               {
                                   parts.Remove(c);
                                   var childNode = new PartNode(c);
                                   node.Childs.Add(childNode);
                                   childNode.Parent = node;
                                   AddPart(childNode);
                                   PopulatePartsNodesRecursive(childNode, parts);
                               });
        }

        private DataGridView AddPart(PartNode node)
        {
            DataGridView result = null;
            if (!_grids.ContainsKey(node.Part))
            {
                ++_tlp.RowCount;
                Label label = new Label();
                label.Text = node.Part.Name;
                //label.AutoSize = true;
                label.Width = 660;
                label.Font = new Font(label.Font.FontFamily, 12, FontStyle.Bold);
                label.Dock = DockStyle.Fill;
                _tlp.RowStyles.Add(new RowStyle(SizeType.AutoSize));
                _tlp.Controls.Add(label, 0, _tlp.RowCount - 1);

                Button btn = new Button();
                btn.Text = "+";
                btn.Width = btn.Height = 20;
                btn.Click += BtnClick;
                _tlp.Controls.Add(btn, 1, _tlp.RowCount - 1);

                ++_tlp.RowCount;
                var grid = CreateGridFromOrderItems(node.Part);
                grid.Visible = false;
                var viewInfo = new OrderPartHelpViewInfo(grid, btn, true);
                btn.Tag = viewInfo;

                _grids.Add(node.Part, viewInfo);
                //grid.Dock = DockStyle.Fill;
                _tlp.RowStyles.Add(new RowStyle(SizeType.AutoSize));
                _tlp.Controls.Add(grid, 0, _tlp.RowCount - 1);
                _tlp.SetColumnSpan(grid, 2);

                result = grid;
            }
            return result;
        }

        private void BtnClick(object sender, EventArgs e)
        {
            Button btn = (Button) sender;
            OrderPartHelpViewInfo viewInfo = (OrderPartHelpViewInfo) btn.Tag;
            //if(viewInfo.Collapsed)
            //    btn.Text = "-";
            //else
            //    btn.Text = "+";

            //viewInfo.Grid.Visible = viewInfo.Collapsed;
            //viewInfo.Collapsed = !viewInfo.Collapsed;
            SetCollapsedState(viewInfo, !viewInfo.Collapsed);
        }

        private void SetCollapsedState(OrderPartHelpViewInfo viewInfo, bool collapsed)
        {
            if(collapsed)
                viewInfo.Btn.Text = "+";
            else
                viewInfo.Btn.Text = "-";

            viewInfo.Grid.Visible = !collapsed;
            viewInfo.Collapsed = collapsed;
        }

        private DataGridView CreateGridFromOrderItems(IOrderPartHelp partHelp)
        {
            DataGridView result = new DataGridView();
            var textBoxTemplate = new DataGridViewTextBoxCell();
            var templates = _templates[partHelp.ID];

            result.Columns.Add(new DataGridViewColumn(templates.Item1) {Name = "ProductName", HeaderText = "Наименование", Width = 200});
            result.Columns.Add(new DataGridViewColumn(templates.Item2) { Name = "MaterialName", HeaderText = "Материал", Width = 200 });
            result.Columns.Add(new DataGridViewColumn(textBoxTemplate) {Name = "Amount", HeaderText = "Количество", Width = 75});
            result.Columns.Add(new DataGridViewColumn(textBoxTemplate) {Name = "Description", HeaderText = "Примечание", Width = 100});
            result.Columns.Add(new DataGridViewColumn(textBoxTemplate) {Name = "Cost", HeaderText = "Цена", Width = 100, ReadOnly = true});
            
            result.Width = 700;
            result.Height = 50;
            result.RowsAdded += OrderItemGridRowsAdded;
            result.RowsRemoved += OrderItemGridRowsRemoved;
            result.ShowEditingIcon = false;
            result.RowHeadersWidth = 20;
            result.DataError += GridDataError;
            result.CellEndEdit += GridCellEndEdit;
            return result;
        }

        private void GridCellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            if (!_inEditMode)
            {
                _inEditMode = true;
                DataGridView grid = (DataGridView) sender;
                DataGridViewRow row = grid.Rows[e.RowIndex];
                DataGridViewColumn column = grid.Columns[e.ColumnIndex];

                if (e.ColumnIndex <= 2)
                {
                    row.Cells["Cost"].Value = null;
                    var strMaterial = (string) row.Cells["MaterialName"].Value;
                    var strProduct = (string) row.Cells["ProductName"].Value;
                    var material = _materials[strMaterial];
                    var product = _products[strProduct];

                    if (product != null)
                    {
                        var priceItem = _priceItems.FirstOrDefault(pi =>
                                                                       {
                                                                           bool result = false;
                                                                           if (pi.Material != null)
                                                                               result = pi.MaterialID == material.ID && pi.ProductID == product.ID;
                                                                           else
                                                                               result = string.IsNullOrEmpty(strMaterial) && pi.ProductID == product.ID;
                                                                           return result;
                                                                       });
                        if (priceItem != null)
                        {
                            object amountCellValue = row.Cells["Amount"].Value;
                            if (amountCellValue != null)
                            {
                                string stringAmountValue = amountCellValue.ToString();
                                int amount;
                                if (int.TryParse(stringAmountValue, out amount))
                                {
                                    row.Cells["Cost"].Value = priceItem.Cost*amount;
                                }
                            }
                            else
                            {
                                row.Cells["Cost"].Value = null;
                            }
                        }
                    }

                    if (product != null)
                        row.Cells["ProductName"].Tag = product;

                    if (material != null)
                        row.Cells["MaterialName"].Tag = material;
                }


                if (row.Tag != null)
                {
                    IOrderItem item = (IOrderItem) row.Tag;
                    switch (column.Name)
                    {
                        case "ProductName":
                            {
                                IProduct product = null;
                                if (_products.TryGetValue((string) (row.Cells[column.Name].Value), out product))
                                {
                                    IMaterial material = item.PriceItem.Material;
                                    //var priceItem = _priceItems.FirstOrDefault(pi => pi.MaterialID == material.ID && pi.ProductID == product.ID);
                                    var priceItem = item.Context.Query<IPriceItem>().FirstOrDefault(pi => pi.Material.ID == material.ID && pi.Product.ID == product.ID);
                                    item.PriceItem = priceItem;
                                }
                            }
                            break;
                        case "MaterialName":
                            {
                                IMaterial material = null;
                                if (_materials.TryGetValue((string) (row.Cells[column.Name].Value), out material))
                                {
                                    IProduct product = item.PriceItem.Product;
                                    //var priceItem = _priceItems.FirstOrDefault(pi => pi.MaterialID == material.ID && pi.ProductID == product.ID);
                                    var priceItem = item.Context.Query<IPriceItem>().FirstOrDefault(pi => pi.Material.ID == material.ID && pi.Product.ID == product.ID);
                                    item.PriceItem = priceItem;
                                }
                            }
                            break;
                        case "Amount":
                            {
                                var value = row.Cells[column.Name].Value;
                                item.Amount = value != null ? (int?)Int32.Parse((string)value) : null;
                            }
                            break;
                        case "Description":
                            {
                                item.Description = (string) (row.Cells[column.Name].Value);
                            }
                            break;
                        case "Cost":
                            break;
                        default:
                            break;
                    }
                }

                if (column.Name == "ProductName")
                {
                    IEnumerable<DataRow> rows = _priceItemsCombinations.Value.Table.Select(string.Format("ProductName = '{0}'", row.Cells[column.Name].Value));

                    var materialsCell = (DataGridViewComboBoxCell) row.Cells["MaterialName"];
                    materialsCell.Items.Clear();
                    
                    if(rows.Count() == 1)
                    {
                        materialsCell.Value = rows.First()["MaterialName"];
                        rows = _priceItemsCombinations.Value.Table.AsEnumerable();
                    }
                    materialsCell.Items.AddRange(rows.Select(r =>
                        {
                            var id = r.Field<Guid?>("Material_ID");
                            if (id == null)
                                return string.Empty;
                            var material = _cacheMaterials[id.Value];
                            return material.Name;
                        }).Distinct().OrderBy(n => n).ToArray());

                }
                else if (column.Name == "MaterialName")
                {
                    IEnumerable<DataRow> rows = _priceItemsCombinations.Value.Table.Select(string.Format("MaterialName = '{0}'", row.Cells[column.Name].Value));

                    var productsCell = (DataGridViewComboBoxCell) row.Cells["ProductName"];
                    productsCell.Items.Clear();

                    if(rows.Count() == 1)
                    {
                        productsCell.Value = rows.First()["ProductName"];
                        rows = _priceItemsCombinations.Value.Table.AsEnumerable();
                    }
                    productsCell.Items.AddRange(rows.Select(r =>
                    {
                        var id = r.Field<Guid?>("Product_ID");
                        if (id == null)
                            return string.Empty;
                        var product = _cacheProducts[id.Value];
                        return product.GetKeyName();
                    }).Distinct().OrderBy(n => n).ToArray());
                }

                _inEditMode = false;
            }
        }

        private void GridDataError(object sender, DataGridViewDataErrorEventArgs e)
        {
        }

        private void OrderItemGridRowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
        {
            DataGridView grid = (DataGridView) sender;
            grid.Height += grid.RowTemplate.Height;
        }

        private void OrderItemGridRowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            DataGridView grid = (DataGridView)sender;
            grid.Height -= grid.RowTemplate.Height;
        }

        public Dictionary<IOrderPartHelp, List<IOrderItem>> FormOrder(Func<IOrderItem> orderItemCreator)
        {
            Dictionary<IOrderPartHelp, List<IOrderItem>> result = null;
            if (Order.CheckSelfProperties())
            {
                result = new Dictionary<IOrderPartHelp, List<IOrderItem>>();
                foreach (var grid in _grids)
                {
                    List<IOrderItem> orderItems = new List<IOrderItem>();
                    bool partAttached = false;
                    foreach (var row in grid.Value.Grid.Rows.Cast<DataGridViewRow>().Where(r => !r.IsNewRow))
                    {
                        if (row.Tag == null)
                        {
                            var orderItem = orderItemCreator.Invoke();
                            var strMaterial = (string) row.Cells["MaterialName"].Value;
                            var strProduct = (string) row.Cells["ProductName"].Value;

                            var material = _materials[strMaterial];
                            var product = _products[strProduct];

                            if (product != null)
                            {
                                var priceItem = _priceItems.FirstOrDefault(pi =>
                                                                               {
                                                                                   bool res = false;
                                                                                   if (pi.Material == null)
                                                                                       res = string.IsNullOrEmpty(strMaterial) &&
                                                                                                pi.ProductID == product.ID;
                                                                                   else
                                                                                       res = pi.MaterialID == material.ID &&
                                                                                                pi.ProductID == product.ID;
                                                                                   return res;
                                                                               });
                                if (priceItem != null)
                                {
                                    orderItem.Context.AttachObject(priceItem);
                                    if (!partAttached)
                                    {
                                        orderItem.Context.AttachObject(grid.Key);
                                        partAttached = true;
                                    }
                                    orderItem.PriceItem = priceItem;
                                    var stringAmountValue = (string) row.Cells["Amount"].Value;
                                    orderItem.Amount = string.IsNullOrWhiteSpace(stringAmountValue) ? (int?) null : int.Parse(stringAmountValue);
                                }
                                orderItems.Add(orderItem);
                            }
                        }
                    }
                    if (orderItems.Any())
                        result.Add(grid.Key, orderItems);
                }
            }
            return result;
        }
    }
}
